﻿using System;
using System.Collections.Generic;
using System.Linq;
using BFlatCompile.Nodes;

namespace BFlatCompile.Serializer
{
	internal static class Phase13SerializeCode
	{
		public static void Do(Dictionary<string, Serializer.Buffer> cellsBySignature, Dictionary<string, int> sizeBySignature, Compiler compiler, Things things)
		{
			SerializeFields(cellsBySignature, sizeBySignature, true, things.StaticFields, compiler);
			SerializeFields(cellsBySignature, sizeBySignature, false, things.Fields, compiler);
			SerializeMethods(cellsBySignature, sizeBySignature, true, things.StaticMethods, compiler);
			SerializeMethods(cellsBySignature, sizeBySignature, false, things.Methods, compiler);
			SerializeProperties(cellsBySignature, sizeBySignature, true, true, things.StaticPropertyGetters, compiler);
			SerializeProperties(cellsBySignature, sizeBySignature, true, false, things.StaticPropertySetters, compiler);
			SerializeProperties(cellsBySignature, sizeBySignature, false, true, things.PropertyGetters, compiler);
			SerializeProperties(cellsBySignature, sizeBySignature, false, false, things.PropertySetters, compiler);
			SerializeConstructors(cellsBySignature, sizeBySignature, true, things.StaticConstructors, compiler);
			SerializeConstructors(cellsBySignature, sizeBySignature, false, things.Constructors, compiler);
			SerializeMethodParamDefaultValues(cellsBySignature, sizeBySignature, false, things.MethodParamValues, compiler);
		}

		private static void SerializeConstructors(Dictionary<string, Serializer.Buffer> cells, Dictionary<string, int> size, bool isStatic, IList<Constructor> constructors, Compiler c)
		{
			foreach (Constructor cons in constructors)
			{
				string signature = (isStatic ? "X" : "x") + cons.ID;
				Serializer.Buffer b = new Serializer.Buffer();
				ByteCodeExecSerializer.SerializeLines(c, b, cons.Code);
				b.Add(ByteCodes.STACK_DATA(c,
					c.StackTraceTokenTable.GetId(cons.Filename),
					c.StackTraceTokenTable.GetId(cons.FullSignature)));

				cells[signature] = b;
				size[signature] = b.Size;
			}
		}

		private static void SerializeProperties(Dictionary<string, Serializer.Buffer> cells, Dictionary<string, int> size, bool isStatic, bool isGetter, IList<Property> properties, Compiler c)
		{
			foreach (Property property in properties)
			{
				string signature = null;
				Serializer.Buffer b = new Serializer.Buffer();
				Executable[] code = null;
				if (isGetter)
				{
					if (property.Getter != null)
					{
						signature = (isStatic ? "G" : "g") + property.GetterID;
						code = property.Getter;
					}
					else
					{
						throw new Exception("This will never happen");
					}
				}
				else
				{
					if (property.Setter != null)
					{
						signature = (isStatic ? "S" : "s") + property.SetterID;
						code = property.Setter;
					}
					else
					{
						throw new Exception("This will never happen");
					}
				}

				Serializer.ByteCodeExecSerializer.SerializeLines(c, b, code);

				if (!isGetter)
				{
					// set the variable "value" to the value that was passed in.
					Variable vTemp = new Variable("value", null);
					Serializer.Buffer header = new Serializer.Buffer();
					ByteCodeExprSerializer.SerializeVariable(c, header, vTemp, true);
					header.Add(ByteCodes.APPLY_ARG_N(c, 0));
					if (property.Filename.StartsWith("$"))
					{
						header.Add(ByteCodes.ARG_PRE_VERIFY(c));
					}
					header.Add(ByteCodes.ASSIGN(c));
					header.Add(b);
					b = header;
				}

				b.Add(ByteCodes.STACK_DATA(c,
					c.StackTraceTokenTable.GetId(property.Filename),
					c.StackTraceTokenTable.GetId(property.FullSignature + (isGetter ? "[get]" : "[set]"))));

				cells[signature] = b;
				size[signature] = b.Size;
			}
		}

		private static void SerializeMethods(Dictionary<string, Serializer.Buffer> cells, Dictionary<string, int> size, bool isStatic, IList<Method> methods, Compiler c)
		{
			foreach (Method method in methods)
			{
				string signature = (isStatic ? "M" : "m") + method.ID;
				Serializer.Buffer b = new Serializer.Buffer();
				Serializer.ByteCodeExecSerializer.SerializeLines(c, b, method.Code);
				b.Add(ByteCodes.STACK_DATA(c, c.StackTraceTokenTable.GetId(method.Filename), c.StackTraceTokenTable.GetId(method.FullSignature)));
				size[signature] = b.Size;
				cells[signature] = b;
			}
		}

		private static void SerializeFields(Dictionary<string, Serializer.Buffer> cells, Dictionary<string, int> size, bool isStatic, IList<Field> fields, Compiler c)
		{
			foreach (Field field in fields)
			{
				string signature = (isStatic ? "F" : "f") + field.ID;
				Serializer.Buffer b = new Serializer.Buffer();
				Serializer.ByteCodeExprSerializer.Serialize(c, b, field.DefaultValue);
				size[signature] = b.Size;
				cells[signature] = b;
			}
		}

		private static void SerializeMethodParamDefaultValues(Dictionary<string, Serializer.Buffer> cells, Dictionary<string, int> size, bool isStatic, IList<MethodParam> methodParams, Compiler c)
		{
			foreach (MethodParam p in methodParams)
			{
				string signature = "R" + p.ID;
				Serializer.Buffer b = new Serializer.Buffer();
				ByteCodeExprSerializer.Serialize(c, b, p.Value);
				b.Add(ByteCodes.RETURN(c));
				cells[signature] = b;
				size[signature] = b.Size;
			}
		}
	}
}
